iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0

程式畫面預覽:

圖

範例影片:

影片

程式碼:

import { StatusBar } from 'expo-status-bar';
import React, { useEffect, useState } from 'react';
import {
  StyleSheet,
  SafeAreaView,
  View,
  TextInput,
  Text,
  FlatList,
  TouchableOpacity,
  Alert,
  Keyboard
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import AsyncStorage from '@react-native-async-storage/async-storage';

const COLORS = { primary: "#1f145c", white: "#fff" };

export default function App() {

  const [todos, setTodos] = useState([])

  useEffect(() => {
    getTodoToUserDevice()
  }, [])
  //
  useEffect(() => {
    // saveTodotoUserDevice()
    saveTodoToUserDevice(todos)
  }, [todos])

  const [textInput, setTextInput] = useState("")

  // 新增todo
  const addTodo = () => {
    // console.log(textInput)
    //

    if (textInput == "") {
      Alert.alert("錯誤", "請輸入資料")

    } else {
      const newTodo = {
        id: Math.random(),
        task: textInput,
        completed: false
      }
      // console.log(newTodo)
      setTodos([...todos, newTodo])
      //清空輸入區
      setTextInput("")
      //關閉螢幕鍵盤
      Keyboard.dismiss();
      // console.log(todos)
    }
  }

  // 刪除 todo
  const deleteTodo = (todoId) => {
    // console.log(todoId)
    const newTodos = todos.filter(item => item.id != todoId)
    setTodos(newTodos)
  }

  // 清空 todo
  const clearTodo = () => {
    Alert.alert("確認", "清空待辦事項!", [{
      text: "Yes",
      onPress: () => setTodos([]),
    },
    { text: "No" }])
  }

  // 標示完成
  const markTodoCompleted = (todoId) => {
    //印出id
    console.log(todoId)
    const newTodos = todos.map((item) => {
      if (item.id == todoId) {
        return { ...item, completed: true }
      }
      return item
    })
    setTodos(newTodos)
  }

  //存檔功能
  const saveTodoToUserDevice = async todos => {
    try {
      const stringifyTodos = JSON.stringify(todos);
      await AsyncStorage.setItem('todos', stringifyTodos);
      // console.log("todos= ", todos);
    } catch (error) {
      console.log(error);
    }
  };

  //讀檔功能
  const getTodoToUserDevice = async () => {
    try {
      const todos = await AsyncStorage.getItem("todos")
      if (todos != null) {
        setTodos(JSON.parse(todos))
      }
    } catch (error) {
      console.log(error);
    }
  }

  const ListItem = ({ mytodo }) => {
    return <View style={styles.listItem}>
      {/* 文字區 */}
      <View style={{ flex: 1 }}>
        <Text style={{ fontWeight: "bold", fontSize: 16, color: COLORS.primary, textDecorationLine: mytodo?.completed ? "line-through" : "none" }}>{mytodo?.task}</Text>
      </View>
      {/* 圖示區 */}

      {
        // 打勾的圖示 
        !mytodo?.completed && (
          <TouchableOpacity
            style={[styles.actionIcon]}
            onPress={() => markTodoCompleted(mytodo?.id)}
          >
            <Icon
              name="done"
              size={20}
              color={COLORS.white}
            />
          </TouchableOpacity>
        )
      }

      {/* 刪除的圖示 */}
      <TouchableOpacity
        style={[styles.actionIcon, { backgroundColor: "red" }]}
        onPress={() => deleteTodo(mytodo?.id)}
      >
        <Icon
          name="delete"
          size={20}
          color={COLORS.white}
        />
      </TouchableOpacity>
    </View>
  }

  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: COLORS.white }}>
      {/* 頁首 */}
      <View style={styles.header}>
        <Text style={{ fontWeight: "bold", fontSize: 20, color: COLORS.primary }}>待辦事項</Text>
        {/* 圖示 */}
        <Icon
          name='delete'
          size={26}
          color="red"
          onPress={clearTodo}
        />
      </View>
      {/* 中間展示區塊 */}
      <FlatList
        showsVerticalScrollIndicator={false}
        contentContainerStyle={{ padding: 20, paddingBottom: 100 }}
        data={todos}
        renderItem={
          ({ item }) => <ListItem mytodo={item} />
        }

      />

      {/* 頁尾 */}
      <View style={styles.footer}>
        <View style={styles.inputContainer}>
          <TextInput
            placeholder='請輸入待辦事項'
            onChangeText={(text) => setTextInput(text)}
            value={textInput}

          />
        </View>
        <TouchableOpacity onPress={addTodo}>
          <View style={styles.iconContainer}>
            <Icon
              name="add"
              size={30}
              color="white" />
          </View>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  header: {
    padding: 20,
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
  },
  footer: {
    position: "absolute",
    bottom: 0,
    backgroundColor: COLORS.white,
    width: "100%",
    flexDirection: "row",
    alignItems: "center",
    paddingHorizontal: 20,
  },
  inputContainer: {
    backgroundColor: COLORS.white,
    elevation: 40,
    flex: 1,
    height: 50,
    marginVertical: 20,
    marginRight: 20,
    borderRadius: 30,
    paddingHorizontal: 20,
  },
  iconContainer: {
    width: 50,
    height: 50,
    backgroundColor: COLORS.primary,
    borderRadius: 25,
    elevation: 40,
    alignItems: "center",
    justifyContent: "center",
  },
  listItem: {
    padding: 20,
    backgroundColor: COLORS.white,
    flexDirection: "row",
    elevation: 12,
    borderRadius: 10,
    marginVertical: 10,
  },
  actionIcon: {
    width: 25,
    height: 25,
    backgroundColor: "green",
    alignItems: "center",
    justifyContent: "center",
    marginLeft: 5,
    borderRadius: 5
  },
});

註解:

1. 使用 AsyncStorage 來存儲資料
2. 使用 FlatList 來展示資料區塊
3. ?. 問號點的用法 (可選鏈語法) ()
4. inline style 混合的用法

//inline style 混合用法
套用兩個(或以上) styles
<Text style={[styles.base, styles.background]} >Test </Text>

套用 單行+ styles
<Text style={[styles.base, {color: 'red'}]} >Test </Text>

// .?點問號的用法 (有看沒有懂...XD)
// 程式碼節錄 
!mytodo?.completed && (
    <TouchableOpacity
        style={[styles.actionIcon]}
        onPress={() => markTodoCompleted(mytodo?.id)}
    >
    <Icon
        name="done"
        size={20}
        color={COLORS.white}
    />
    </TouchableOpacity>
)

範例 Source Code :

git clone https://smilehsu@bitbucket.org/smilehsu/rn_example_todolist_2.git

參考資料:


上一篇
DAY06 - 看YT學React Native - ToDoList 範例1
下一篇
DAY08 - 如何更新現有專案的EXPO SDK版本
系列文
總是學不來的中年大叔,孤獨的自學之旅第二年30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言